#include "cybertron/core/UtilUrlRequest.hpp"

#include "TaskSimulinkRun.hpp"

#include "../Util/BridgeIOConfig.hpp"

#include "cybertron/core/Log.hpp"
#include "cybertron/core/UtilPath.hpp"
#include "cybertron/core/UtilFile.hpp"
#include "cybertron/core/UtilDirectory.hpp"
#include "cybertron/core/UtilProcess.hpp"
#include <thread>
#ifdef CYBERTRON_WIN
#include <windows.h>
#include <tlhelp32.h>
#else
#include <spawn.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/wait.h>
#endif
#pragma warning(disable:4996)

CYBERTRON_BEGIN

std::string GetTmpFileFromUrl(const std::string& url)
{
    std::string localTmpFile;
    if (url.empty()) {
        return localTmpFile;
    }

    std::string tmpDir = cybertron::UtilDirectory::getTempFolder();
    // get file name in URL
    std::string filename = "";
    std::string::size_type pos = url.rfind('/');
    if (pos != std::string::npos) {
        filename = url.substr(pos + 1);
    }
    localTmpFile = cybertron::UtilPath::combine(tmpDir, filename);

    std::string fileContent;
    {
        try {
            fileContent = UtilUrlRequest::GetUrl(url);
            FILE * fp = fopen(localTmpFile.c_str(), "wb+");
            size_t wbyte = fwrite(fileContent.c_str(), fileContent.size(), 1, fp);
            fclose(fp);
        }
        catch (const UtilUrlRequest::TException&) {
            localTmpFile.clear();
        }
    }

    return localTmpFile;
}

TaskControlModuleRun::TaskControlModuleRun(std::string hostVehicleId)
{
    setName("TaskUserControlModule");
	
    setTaskType(proto::sensor::EBridgeTaskType_UserControlModule);
	
    // download module
	BridgeConfig &config = BridgeConfig::getInstance();
	auto hostVehicleConfig = config.getHostVehicleConfig();
	const BridgeConfig::CustomizationModule* custModule = config.customizationModuleConf(hostVehicleId);
	if (custModule->platform == "windows") {
		mbRunExeIsWindows = true;
	}
	else {
		mbRunExeIsWindows = false;
	}
	if (custModule->langType == "interpreted") {
		mIsPython = true;
	}
	else {
		mIsPython = false;
	}

	SimOne_Data_CaseInfo* pCaseInfo = config.getCaseInfo();
	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "Download module url from: %s", custModule->url.c_str());
	std::string urlFile = cybertron::GetTmpFileFromUrl(custModule->url);
	if (!cybertron::UtilFile::exist(urlFile)) {
		logInfo("Fail to download module: %s", urlFile.data());
	}
	if (!urlFile.empty()) {
		logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "Download module file to: %s", urlFile.data());
		if (custModule->suffix == "zip") {
			std::string zipFilePath = cybertron::UtilPath::getPathWithoutFilename(urlFile);
			string temp = cybertron::UtilPath::combine(zipFilePath, pCaseInfo->taskId);
			mUnpackDir = cybertron::UtilPath::combine(temp, custModule->target);
			// unzip file
			if (unzipFile(urlFile, mUnpackDir)) {
				logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "Unzip zip file to %s", mUnpackDir.data());
				mModuleFile = cybertron::UtilPath::combine(mUnpackDir, custModule->entry);
				if (!cybertron::UtilFile::exist(mModuleFile)) {
					logOutputError(BridgeConfig::getInstance().getTaskId().c_str(), "Fail to find executable file: %s", mModuleFile.data());
					mModuleFile.clear();
				}
				else {
#if CYBERTRON_LINUX
					std::string cmd = "chmod a+x ";
					cmd += mModuleFile;
					system(cmd.data());
#endif
					if (!mIsPython) {
						mModuleFile = cybertron::UtilPath::getPathWithoutFileExtension(mModuleFile);
					}


					logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "Get executable file without suffix: %s", mModuleFile.data());
				}
			}
			else {
				logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Fail to unzip zip file: %s, %s", urlFile.data(), mUnpackDir.c_str());
			}
			//std::remove(urlFile.data());
		}
		else if (custModule->suffix == "exe") {
			mModuleFile = cybertron::UtilPath::getPathWithoutFileExtension(urlFile);
			logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "Get executable file without suffix: %s", mModuleFile.data());
		}
		else {
			logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Unknow file suffix: %s", custModule->suffix.data());
		}
	}
	else {
		logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Fail to download module file from URL: %s", custModule->url.data());
	}
}

TaskControlModuleRun::~TaskControlModuleRun()
{
    // remove module file
    if (!mModuleFile.empty()) {
        //std::remove(mModuleFile.c_str());
        if (!mUnpackDir.empty()) {
#if CYBERTRON_WIN
            std::string cmd = "rmdir /S /Q ";
            cmd += mUnpackDir;
            system(cmd.data());
#else
            std::string cmd = "rm -rf ";
            cmd += mUnpackDir;
            system(cmd.data());
#endif
        }
    }
}

bool TaskControlModuleRun::onStart()
{
	setHz(60);

	if (mModuleFile.empty()) {
		logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(),"Fail to download module File");
		return false;
	}
	return runModule();
}

uint16_t TaskControlModuleRun::onFrame(uint64_t frame)
{

	return 1;
}

void TaskControlModuleRun::onEnd()
{
    cybertron::UtilProcess::terminateAndCloseHandle(mProcessHandle);
}

void TaskControlModuleRun::runSystemCmd() {
#ifdef CYBERTRON_LINUX
	string workPath = cybertron::UtilPath::getPathWithoutFilename(mModuleFile);

	pid_t pid;
	int status;

	if ((pid = fork()) < 0) {
		status = -1; //forkʧ�ܣ�����-1
	}
	else if (pid == 0) {
		std::string cmd = "dos2unix " + mModuleFile;
		logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "workPath:%s=====>:%s", workPath.c_str(), cmd.data());
		int ret = system(cmd.data());

		//system("")
		chdir(workPath.c_str());
		execl("/bin/sh", "sh", "-c", mModuleFile.c_str(), (char *)0);
		_exit(127); // execִ��ʧ�ܷ���127��ע��execֻ��ʧ��ʱ�ŷ������ڵĽ��̣��ɹ��Ļ����ڵĽ��̾Ͳ�����
	}
	else { //������
		printf("pid====>%d\n", pid);
		mProcessHandle.mHandle = pid;
		while (waitpid(pid, &status, 0) < 0) {
			if (errno != EINTR) {
				status = -1; //���waitpid���ź��жϣ��򷵻�-1
				break;
			}
		}
	}
#else
	string workPath = cybertron::UtilPath::getPathWithoutFilename(mModuleFile);

	std::vector<std::string> params;
	std::vector<std::string> envs;
	if (!UtilProcess::startWindowsBat(mModuleFile, workPath, params, mProcessHandle, envs)) {
		logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Can not start script script is:%s", mModuleFile.c_str());
	}
#endif



	return ;
}
bool TaskControlModuleRun::runModule()
{	
	if (mModuleFile.empty()) {
		logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "runModule  mModuleFile is null");
		return false;
	}
	if (mIsPython) {
#if CYBERTRON_LINUX
#else
		//string path = cybertron::UtilPath::getPathWithoutFilename(mModuleFile);

		//std::vector<std::string> params;
		//std::vector<std::string> envs;
		//if (!UtilProcess::startWindowsBat(mModuleFile, path, params, mProcessHandle, envs)) {
		//	logOutputWarn("Can not start script script is:%s", mModuleFile.c_str());
		//}
#endif

		std::thread thread(&TaskControlModuleRun::runSystemCmd, this);
		thread.detach();
		return 0;
	}
	
    std::vector<std::string> params;
	std::vector<std::string> envs;

	std::string moduleDir = cybertron::UtilPath::getPathWithoutFilename(mModuleFile);
#if CYBERTRON_LINUX
        envs.push_back(moduleDir );
		if (mbRunExeIsWindows) {
			//wine
			logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(),"linux to run  wine");

			std::vector<std::string> paramsWithExe;
			paramsWithExe.push_back(mModuleFile);
			for (auto it = params.begin(); it != params.end(); it++)
				paramsWithExe.push_back((*it));
			params = paramsWithExe;
			std::string processPath = std::string("wine");
			logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "processPath:%s", processPath.c_str());
			logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "getWorkingPath:%s", cybertron::UtilPath::getWorkingPath().c_str());
			//for (auto n : params)
			//{
			//	logOutputInfo("params:%s", n.c_str());
			//}
			//for (auto n : envs)
			//{
			//	logOutputInfo("envs:%s", n.c_str());
			//}
			if (!cybertron::UtilProcess::startex(
				processPath,
				cybertron::UtilPath::getWorkingPath(),
				params,
				mProcessHandle,
				envs)) {
				logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "linux start failed");
				return false;
			}

		}
		else {
			logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "linux to run  exe");
			if (!cybertron::UtilProcess::startex(
				mModuleFile,
				cybertron::UtilPath::getWorkingPath(),
				params,
				mProcessHandle, envs)) {
				logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "linux start failed");
				return false;
			}
			return true;
		}
#else
	if (!mbRunExeIsWindows) {
		logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "windows is not supported");
		return false;
	}
	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "windows to run  exe");
	if (!cybertron::UtilProcess::startex(
		mModuleFile,
		cybertron::UtilPath::getWorkingPath(),
		params,
		mProcessHandle, envs)) {
		logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "windows start failed");
		return false;
	}
	return true;
#endif

        
	
}

void TaskControlModuleRun::bridgeOnTimer(NodeHotAreaManager &mgr, int ms, int msModNs)
{
    // check process
    if (!UtilProcess::isRunning(mProcessHandle)) {
        // send message and terminate
        logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Control Module is not running, stop test");
        mgr.endTaskToNodeTime(Common::ETaskEndReason_UserControlModuleTerminated);
    }
}

bool TaskControlModuleRun::unzipFile(const std::string& urlFile, const std::string& dir)
{
	
    std::string exePath = cybertron::UtilPath::getExecutablePath();
	
#if CYBERTRON_WIN
    std::string unzipExe = cybertron::UtilPath::combine(exePath, "../../../../../../3rdparty/7zip/win64/7z.exe");

	//unzipExe = cybertron::UtilPath::combine(exePath, "7z.exe");
	//logOutputInfo("unzip executable 33%s", unzipExe.c_str());
	//if (!cybertron::UtilFile::exist(unzipExe)) {
	//	logOutputWarn("Fail to find unzip executable 44");
	//}

	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip executable 11%s", unzipExe.c_str());
    if (!cybertron::UtilFile::exist(unzipExe)) {
		logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip executable 22%s", unzipExe.c_str());
        unzipExe = cybertron::UtilPath::combine(exePath, "../../Tools/Zip/7z.exe");
        if (!cybertron::UtilFile::exist(unzipExe)) {
			unzipExe = cybertron::UtilPath::combine(exePath, "7z.exe");
			logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip executable 33%s", unzipExe.c_str());
			if (!cybertron::UtilFile::exist(unzipExe)) {
				logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Fail to find unzip executable 44");
			}
            logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Fail to find unzip executable 55");
            return false;
        }
    }
    std::string cmd = unzipExe + " e -y -o" + dir + " " + urlFile;
	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip command:%s", cmd.data());
    int ret = system(cmd.data());
    return ret == 0;
#else
    std::string unzipExe = cybertron::UtilPath::combine(exePath, "../../../../../3rdparty/7zip/linux/7za");
	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip executable 11 %s", unzipExe.c_str());
    if (!cybertron::UtilFile::exist(unzipExe)) {
	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip executable 22  %s", unzipExe.c_str());
        unzipExe = cybertron::UtilPath::combine(exePath, "../../Tools/Zip/7za");
        if (!cybertron::UtilFile::exist(unzipExe)) {
		unzipExe = cybertron::UtilPath::combine(exePath, "7za");
		logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip executable 33 %s %s", unzipExe.c_str(), dir.c_str());
		if (!cybertron::UtilFile::exist(unzipExe)) {
			logOutputWarn(BridgeConfig::getInstance().getTaskId().c_str(), "Fail to find unzip executable");
    			return false;
		}
        }
    }
    std::string cmd = unzipExe + " e -y -o" + dir + " " + urlFile;
	logOutputInfo(BridgeConfig::getInstance().getTaskId().c_str(), "unzip command:%s", cmd.data());
    int ret = system(cmd.data());
    return ret == 0;
#endif
}

CYBERTRON_END
